﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows.Forms;

using Storm.TextEditor.Editor;
using Storm.TextEditor.Parser.Objects;
using Storm.TextEditor.Parser.Objects.Collections;
using Storm.TextEditor.Parser.XML;

namespace Storm.TextEditor.Parser
{
    /// <summary>
    /// Represents the default parser that parses languages.
    /// </summary>
    public sealed class DefaultParser
        : IParser
    {
        #region Fields

        private const string syntaxFileExtension = ".lang";

        private Language language = null;
        private SyntaxDocument document = null;
        private long version = 0;

        #endregion

        #region Properties

        /// <summary>
        /// Gets or sets the language of the DefaultParser.
        /// </summary>
        public Language Language
        {
            get { return language; }
            set
            {
                language = value;
                this.Document.Reparse();
            }
        }

        /// <summary>
        /// Gets or sets the separators the DefaultParser should use when parsing.
        /// </summary>
        public string Separators
        {
            get { return this.Language.Separators; }
            set { this.Language.Separators = value; }
        }

        /// <summary>
        /// Gets or sets the document of the DefaultParser.
        /// </summary>
        public SyntaxDocument Document
        {
            get { return document; }
            set { document = value; }
        }

        #endregion

        #region Methods

        #region Private

        /// <summary>
        /// Makes the row at the given row the same as the row below it.
        /// </summary>
        /// <param name="rowIndex">Index of the row.</param>
        private void MakeSame(int rowIndex)
        {
            try
            {
                Row row = this.Document[rowIndex];

                Segment startSegment = this.Document[rowIndex + 1].StartSegment;
                Segment endSegment = row.EndSegment;

                while (endSegment != null)
                {
                    foreach (Word word in row)
                    {
                        if (word.Segment != endSegment)
                            continue;

                        if (word.Segment.StartWord == word)
                            startSegment.StartWord = word;

                        if (word.Segment.EndWord == word)
                            startSegment.EndWord = word;

                        word.Segment = startSegment;
                    }

                    if (endSegment == row.StartSegment)
                        row.StartSegment = startSegment;

                    if (endSegment == row.EndSegment)
                        row.EndSegment = startSegment;

                    if (row.StartSegments.IndexOf(endSegment) >= 0)
                        row.StartSegments[row.StartSegments.IndexOf(endSegment)] = startSegment;

                    if (row.EndSegments.IndexOf(endSegment) >= 0)
                        row.EndSegments[row.EndSegments.IndexOf(endSegment)] = startSegment;

                    endSegment = endSegment.Parent;
                    startSegment = startSegment.Parent;
                }

                row.SetExpansionSegment();
            }
            catch { }
        }

        /// <summary>
        /// Returns whether the row at the given index is the same as the row below it.
        /// </summary>
        /// <param name="rowIndex">Index of the row.</param>
        /// <param name="oldStartSegment">Old starting Segment.</param>
        /// <returns>Whether the row is the same as the row below it.</returns>
        private bool IsSameButDifferent(int rowIndex, Segment oldStartSegment)
        {
            if (rowIndex >= this.Document.Count - 1)
                return false;

            Row row = this.Document[rowIndex];

            Segment endSegment = row.EndSegment;
            Segment oldNextStartSegment = this.Document[rowIndex + 1].StartSegment;

            bool different = false;
            while (endSegment != null)
            {
                if (oldNextStartSegment == null)
                {
                    different = true;
                    break;
                }

                if (endSegment.Block != oldNextStartSegment.Block)
                {
                    different = true;
                    break;
                }

                if (endSegment.Parent != oldNextStartSegment.Parent)
                {
                    different = true;
                    break;
                }

                endSegment = endSegment.Parent;
                oldNextStartSegment = oldNextStartSegment.Parent;
            }

            if (different || row.StartSegment != oldStartSegment)
                return false;

            return true;
        }

        /// <summary>
        /// Returns the next word from the given position.
        /// </summary>
        /// <param name="text">Text to find.</param>
        /// <param name="currentSegment">The current Segment.</param>
        /// <param name="startPosition">Starting position of the search.</param>
        /// <param name="hasComplex">Whether the word is complex.</param>
        /// <returns>The found word.</returns>
        private WordScanResult GetNextWord(string text, Segment currentSegment, int startPosition, ref bool hasComplex)
        {
            Block block = currentSegment.Block;

            #region ComplexFind

            int bestComplexPos = -1;
            Pattern bestComplexPattern = null;

            string bestComplexToken = "";
            WordScanResult complexWordScanResult = new WordScanResult();

            if (hasComplex == true)
            {
                foreach (Pattern pattern in block.ComplexPatterns)
                {
                    PatternScanResult patternScanResult = pattern.IndexIn(text, startPosition, pattern.Parent.CaseSensitive, this.Separators);
                    if (patternScanResult.Token == "")
                        continue;

                    if (patternScanResult.Index >= bestComplexPos && bestComplexPos != -1)
                        continue;

                    bestComplexPos = patternScanResult.Index;
                    bestComplexPattern = pattern;
                    bestComplexToken = patternScanResult.Token;
                }

                if (bestComplexPattern != null)
                {
                    complexWordScanResult.HasContent = true;
                    complexWordScanResult.ParentList = bestComplexPattern.Parent;
                    complexWordScanResult.Pattern = bestComplexPattern;
                    complexWordScanResult.Position = bestComplexPos;
                    complexWordScanResult.Token = bestComplexToken;

                    hasComplex = true;
                }
                else
                    hasComplex = false;
            }

            #endregion

            #region SimpleFind

            WordScanResult simpleWordScanResult = new WordScanResult();
            for (int index = startPosition; index < text.Length; index++)
            {
                if (index > complexWordScanResult.Position && complexWordScanResult.HasContent == true)
                    break;

                if (index <= text.Length - 3)
                {
                    string key = text.Substring(index, 3).ToLower();
                    PatternCollection patternCollection = block.LookupTable[key] as PatternCollection;
                    if (patternCollection != null)
                    {
                        foreach (Pattern pattern in patternCollection)
                        {
                            int patternLength = pattern.StringPattern.Length;
                            if (index + patternLength > text.Length)
                                continue;

                            char lastPatternChar = char.ToLower(pattern.StringPattern[patternLength - 1]);
                            char lastTextChar = char.ToLower(text[index + patternLength - 1]);

                            #region Case Insensitive

                            if (lastPatternChar != lastTextChar)
                                continue;

                            if (pattern.IsKeyword != false && (pattern.IsKeyword != true || pattern.HasSeparators(text, index) != true))
                                continue;

                            if (pattern.Parent.CaseSensitive == false)
                            {
                                string caseInsensitiveText = text.Substring(index, patternLength).ToLower();

                                if (caseInsensitiveText != pattern.StringPattern.ToLower())
                                    continue;

                                simpleWordScanResult.HasContent = true;
                                simpleWordScanResult.ParentList = pattern.Parent;
                                simpleWordScanResult.Pattern = pattern;
                                simpleWordScanResult.Position = index;

                                simpleWordScanResult.Token = pattern.Parent.NormalizeCase == true ? pattern.StringPattern :
                                    text.Substring(index, patternLength);

                                break;
                            }
                            else
                            {
                                string caseInsensitiveText = text.Substring(index, patternLength);

                                if (caseInsensitiveText != pattern.StringPattern)
                                    continue;

                                simpleWordScanResult.HasContent = true;
                                simpleWordScanResult.ParentList = pattern.Parent;
                                simpleWordScanResult.Pattern = pattern;
                                simpleWordScanResult.Position = index;
                                simpleWordScanResult.Token = pattern.StringPattern;

                                break;
                            }

                            #endregion
                        }
                    }
                }

                if (simpleWordScanResult.HasContent == true)
                    break;

                char c = text[index];
                PatternCollection exPatternCollection = block.LookupTable[c] as PatternCollection;

                if (exPatternCollection == null)
                    continue;

                foreach (Pattern pattern in exPatternCollection)
                {
                    int patternLength = pattern.StringPattern.Length;
                    if (index + patternLength > text.Length)
                        continue;

                    char lastPatternChar = pattern.StringPattern[patternLength - 1];
                    char lastTextChar = text[index + patternLength - 1];

                    if (pattern.Parent.CaseSensitive == false)
                    {
                        #region Case Insensitive

                        if (char.ToLower(lastPatternChar) == char.ToLower(lastTextChar))
                        {
                            if (pattern.IsKeyword == false || (pattern.IsKeyword == true && pattern.HasSeparators(text, index) == true))
                            {
                                string caseInsensitiveText = text.Substring(index, patternLength).ToLower();
                                if (caseInsensitiveText != pattern.StringPattern.ToLower())
                                    continue;

                                simpleWordScanResult.HasContent = true;
                                simpleWordScanResult.ParentList = pattern.Parent;
                                simpleWordScanResult.Pattern = pattern;
                                simpleWordScanResult.Position = index;
                                simpleWordScanResult.Token = text.Substring(index, patternLength);

                                break;
                            }
                        }

                        #endregion
                    }
                    else
                    {
                        #region Case Sensitive

                        if (lastPatternChar == lastTextChar)
                        {
                            if (pattern.IsKeyword == false || (pattern.IsKeyword == true && pattern.HasSeparators(text, index) == true))
                            {
                                string caseSensitiveText = text.Substring(index, patternLength);
                                if (caseSensitiveText != pattern.StringPattern)
                                    continue;

                                simpleWordScanResult.HasContent = true;
                                simpleWordScanResult.ParentList = pattern.Parent;
                                simpleWordScanResult.Pattern = pattern;
                                simpleWordScanResult.Position = index;
                                simpleWordScanResult.Token = pattern.StringPattern;

                                break;
                            }
                        }

                        #endregion
                    }
                }

                if (simpleWordScanResult.HasContent == true)
                    break;
            }

            #endregion

            #region Finding Correct Values & Returning

            if (complexWordScanResult.HasContent == true && simpleWordScanResult.HasContent == true)
            {
                if (simpleWordScanResult.Position == complexWordScanResult.Position)
                    return simpleWordScanResult.Token.Length > complexWordScanResult.Token.Length ? simpleWordScanResult : complexWordScanResult;

                if (simpleWordScanResult.Position < complexWordScanResult.Position)
                    return simpleWordScanResult;

                if (simpleWordScanResult.Position > complexWordScanResult.Position)
                    return complexWordScanResult;
            }

            if (simpleWordScanResult.HasContent == true)
                return simpleWordScanResult;

            if (complexWordScanResult.HasContent == true)
                return complexWordScanResult;

            return new WordScanResult();

            #endregion
        }

        /// <summary>
        /// Parses the given row from the given segment for the given text.
        /// </summary>
        /// <param name="row">Row to parse.</param>
        /// <param name="currentSegment">The current Segment.</param>
        /// <param name="text">Text to parse for.</param>
        private void ParseText(Row row, Segment currentSegment, string text)
        {
            int currentPosition = 0;
            bool hasComplex = true;

            while (true)
            {
                WordScanResult wordScanResult = this.GetNextWord(text, currentSegment, currentPosition, ref hasComplex);

                if (wordScanResult.HasContent == false)
                {
                    ParseTools.AddString(text.Substring(currentPosition), row, currentSegment.Block.Style, currentSegment);
                    break;
                }
                else
                {
                    ParseTools.AddString(text.Substring(currentPosition, wordScanResult.Position - currentPosition), row, currentSegment.Block.Style, currentSegment);
                    ParseTools.AddPatternString(wordScanResult.Token, row, wordScanResult.Pattern, wordScanResult.ParentList.Style, currentSegment, false);
                    currentPosition = wordScanResult.Position + wordScanResult.Token.Length;
                }
            }
        }

        /// <summary>
        /// Returns the next complex word containing the given text from the starting position.
        /// </summary>
        /// <param name="text">Text to find.</param>
        /// <param name="currentSegment">The current Segment.</param>
        /// <param name="startPosition">Starting position.</param>
        /// <returns>The found word.</returns>
        private WordScanResult GetNextComplexWord(string text, Segment currentSegment, int startPosition)
        {
            if (startPosition >= text.Length)
                return new WordScanResult();

            WordScanResult wordScanResult = new WordScanResult();
            int currentPosition = 0;

            PatternListList keywordsList = currentSegment.Block.KeywordsList;
            PatternList patternList = null;

            for (int i = 0; i < keywordsList.Count; i++)
            {
                patternList = keywordsList[i];

                PatternCollection complexPatterns = patternList.ComplexPatterns;
                Pattern wordPattern = null;

                for (int j = 0; j < complexPatterns.Count; j++)
                {
                    wordPattern = complexPatterns[j];

                    PatternScanResult patternScanResult = wordPattern.IndexIn(text, startPosition, false, this.Separators);
                    currentPosition = patternScanResult.Index;
                    if (currentPosition >= wordScanResult.Position && wordScanResult.HasContent != false || patternScanResult.Token == "")
                        continue;

                    wordScanResult.HasContent = true;
                    wordScanResult.Position = currentPosition;
                    wordScanResult.Token = patternScanResult.Token;
                    wordScanResult.Pattern = wordPattern;
                    wordScanResult.ParentList = patternList;

                    if (patternList.NormalizeCase == true)
                    {
                        if (wordPattern.IsComplex == false)
                            wordScanResult.Token = wordPattern.StringPattern;
                    }
                }
            }

            PatternListList operatorList = currentSegment.Block.OperatorsList;
            PatternList childOperatorList = null;

            for (int i = 0; i < operatorList.Count; i++)
            {
                childOperatorList = operatorList[i];
                PatternCollection complexPatterns = childOperatorList.ComplexPatterns;

                for (int j = 0; j < complexPatterns.Count; j++)
                {
                    Pattern wordPattern = complexPatterns[j];
                    PatternScanResult patternScanResult = wordPattern.IndexIn(text, startPosition, false, Separators);
                    currentPosition = patternScanResult.Index;

                    if (currentPosition >= wordScanResult.Position && wordScanResult.HasContent != false || patternScanResult.Token == "")
                        continue;

                    wordScanResult.HasContent = true;
                    wordScanResult.Position = currentPosition;
                    wordScanResult.Token = patternScanResult.Token;
                    wordScanResult.Pattern = wordPattern;
                    wordScanResult.ParentList = childOperatorList;

                    if (childOperatorList.NormalizeCase == true)
                    {
                        if (wordPattern.IsComplex == false)
                            wordScanResult.Token = wordPattern.StringPattern;
                    }
                }
            }

            return wordScanResult.HasContent ? wordScanResult : new WordScanResult();
        }

        /// <summary>
        /// Parses the line at the given index.
        /// </summary>
        /// <param name="index">Index of the line.</param>
        /// <param name="parseKeywords">Whether to parse keywords.</param>
        private void InternalParseLine(int index, bool parseKeywords)
        {
            if (language == null)
                return;

            SyntaxDocument document = this.Document;
            Row row = document[index];

            Segment oldEndSegment = row.EndSegment;
            Segment oldStartSegment = row.StartSegment;
            bool fold = row.IsCollapsed == false;

            if (row.IsCollapsedEndPart == true)
                row.ExpansionEndSegment.EndWord = null;

            if (index > 0)
                row.StartSegment = this.Document[index - 1].EndSegment;
            else if (row.StartSegment == null)
            {
                row.StartSegment = new Segment(row);
                row.StartSegment.Block = language.MainBlock;
            }

            int currentPosition = 0;
            Segment currentSegment = row.StartSegment;

            row.StartSegments.Clear();
            row.EndSegments.Clear();
            row.Clear();

            while (true)
            {
                SegmentScanResult childSegment = this.GetNextChildSegment(row, currentSegment, currentPosition);
                SegmentScanResult endSegment = this.GetEndSegment(row, currentSegment, currentPosition);

                if ((endSegment.HasContent == true && childSegment.HasContent == true && endSegment.Position <= childSegment.Position) ||
                    (endSegment.HasContent == true && childSegment.HasContent == false))
                {
                    if (parseKeywords == true)
                    {
                        string text = row.Text.Substring(currentPosition, endSegment.Position - currentPosition);
                        this.ParseText(row, currentSegment, text);
                    }

                    Segment oldSegment = currentSegment;
                    while (currentSegment != endSegment.Segment)
                    {
                        row.EndSegments.Add(currentSegment);
                        currentSegment = currentSegment.Parent;
                    }

                    row.EndSegments.Add(currentSegment);

                    TextStyle textStyle = currentSegment.Scope.Style;
                    ParseTools.AddPatternString(endSegment.Token, row, endSegment.Pattern, textStyle, currentSegment, false);

                    while (oldSegment != endSegment.Segment)
                    {
                        oldSegment.EndRow = row;
                        oldSegment.EndWord = row[row.Count - 1];
                        oldSegment = oldSegment.Parent;
                    }

                    currentSegment.EndRow = row;
                    currentSegment.EndWord = row[row.Count - 1];

                    if (currentSegment.Parent != null)
                        currentSegment = currentSegment.Parent;

                    currentPosition = endSegment.Position + endSegment.Token.Length;
                }
                else if (childSegment.HasContent == true)
                {
                    if (parseKeywords == true)
                    {
                        string text = row.Text.Substring(currentPosition, childSegment.Position - currentPosition);
                        ParseText(row, currentSegment, text);
                    }

                    Segment newSegment = new Segment { Parent = currentSegment, Block = childSegment.Block, Scope = childSegment.Scope };
                    row.StartSegments.Add(newSegment);

                    TextStyle style = newSegment.Scope.Style;
                    if (newSegment.Scope.Modifiers.Count > 0)
                    {
                        int tokenIndex = row.Text.IndexOf(childSegment.Token);
                        string testText = row.Text.Substring(0, tokenIndex);
                        row.MayHaveFoldMarker = false;
                        foreach (string modifier in newSegment.Scope.Modifiers)
                        {
                            if (modifier.Length == 0)
                            {
                                if (row.Text.Trim().StartsWith(newSegment.Scope.Start.StringPattern) == true)
                                {
                                    row.MayHaveFoldMarker = true;
                                    break;
                                }
                            }
                            else if (testText.Trim().Length >= modifier.Length)
                            {
                                string modifierText = testText.Trim().Substring(0, modifier.Length);
                                if (modifierText == modifier)
                                {
                                    row.MayHaveFoldMarker = true;
                                    break;
                                }
                            }
                        }
                    }

                    ParseTools.AddPatternString(childSegment.Token, row, childSegment.Pattern, style, newSegment, false);
                    newSegment.StartRow = row;
                    newSegment.StartWord = row[row.Count - 1];

                    currentSegment = newSegment;
                    currentPosition = childSegment.Position + childSegment.Token.Length;

                    if (childSegment.Scope.SpawnBlockOnStart != null)
                    {
                        Segment spawnSegment = new Segment
                        {
                            Parent = newSegment,
                            Block = childSegment.Scope.SpawnBlockOnStart,
                            Scope = new Scope(),
                            StartWord = newSegment.StartWord
                        };

                        row.StartSegments.Add(spawnSegment);
                        currentSegment = spawnSegment;
                    }
                }
                else
                {
                    if (currentPosition < row.Text.Length)
                    {
                        if (parseKeywords == true)
                        {
                            string text = row.Text.Substring(currentPosition);
                            this.ParseText(row, currentSegment, text);
                        }
                    }

                    break;
                }
            }

            while (currentSegment.Block.Multiline == false)
            {
                row.EndSegments.Add(currentSegment);
                currentSegment = currentSegment.Parent;
            }

            row.EndSegment = currentSegment;
            row.SetExpansionSegment();

            row.RowState = parseKeywords == true ? RowState.AllParsed : RowState.SegmentParsed;

            if (this.IsSameButDifferent(index, row.StartSegment) == true)
                this.MakeSame(index);

            if (row.CanFold == true)
                row.ExpansionStartSegment.Expanded = fold;

            if (parseKeywords == false)
            {
                if (oldEndSegment != null)
                {
                    if (row.EndSegment != oldEndSegment && index <= Document.Count - 2)
                    {
                        this.Document[index + 1].AddToParseQueue();
                        this.Document.ShouldResetRows = true;
                    }
                }
                else if (index <= Document.Count - 2)
                {
                    this.Document[index + 1].AddToParseQueue();
                    this.Document.ShouldResetRows = true;
                }
            }

            if (oldEndSegment != null)
            {
                if (oldEndSegment.EndWord == null)
                    oldEndSegment.Expanded = true;
            }
        }

        /// <summary>
        /// Returns the ending segment of the given row.
        /// </summary>
        /// <param name="row">Row to find ending segment in.</param>
        /// <param name="currentSegment">The current segment.</param>
        /// <param name="startPosition">Starting position.</param>
        /// <returns>The found segment.</returns>
        private SegmentScanResult GetEndSegment(Row row, Segment currentSegment, int startPosition)
        {
            if (startPosition >= row.Text.Length || currentSegment.Scope == null)
                return new SegmentScanResult();

            SegmentScanResult segmentScanResult = new SegmentScanResult { HasContent = false, IsEndSegment = false };

            Segment segment = currentSegment;
            while (segment != null)
            {
                if (segment == currentSegment || segment.Block.TerminateChildren)
                {
                    foreach (Pattern endPattern in segment.Scope.EndPatterns)
                    {
                        PatternScanResult patternScanResult = endPattern.IndexIn(row.Text, startPosition, segment.Scope.CaseSensitive, this.Separators);
                        int currentPosition = patternScanResult.Index;
                        if (patternScanResult.Token == "")
                            continue;

                        if ((patternScanResult.Index >= segmentScanResult.Position || !segmentScanResult.HasContent) && segmentScanResult.HasContent != false)
                            continue;

                        segmentScanResult.Pattern = endPattern;
                        segmentScanResult.Position = currentPosition;
                        segmentScanResult.Token = patternScanResult.Token;
                        segmentScanResult.HasContent = true;
                        segmentScanResult.Segment = segment;
                        segmentScanResult.Scope = null;

                        if (endPattern.IsComplex == false)
                        {
                            if (segment.Scope.NormalizeCase == true)
                            {
                                if (segment.Scope.Start.IsComplex == false)
                                    segmentScanResult.Token = endPattern.StringPattern;
                            }
                        }
                    }
                }

                segment = segment.Parent;
            }

            if (segmentScanResult.HasContent == false)
                return new SegmentScanResult();

            return segmentScanResult;
        }

        /// <summary>
        /// Returns the next child segment of the row.
        /// </summary>
        /// <param name="row">Row to find child segment in.</param>
        /// <param name="currentSegment">The current segment.</param>
        /// <param name="startPosition">Starting position.</param>
        /// <returns>The found segment.</returns>
        private SegmentScanResult GetNextChildSegment(Row row, Segment currentSegment, int startPosition)
        {
            return this.GetNextChildSegment(row, currentSegment, startPosition, -1);
        }

        /// <summary>
        /// Returns the next child segment of the row.
        /// </summary>
        /// <param name="row">Row to find child segment in.</param>
        /// <param name="currentSegment">The current segment.</param>
        /// <param name="startPosition">Starting position.</param>
        /// <param name="maxIndex">Maximum index.</param>
        /// <returns>The found segment.</returns>
        private SegmentScanResult GetNextChildSegment(Row row, Segment currentSegment, int startPosition, int maxIndex)
        {
            if (startPosition >= row.Text.Length)
                return new SegmentScanResult();

            SegmentScanResult segmentScanResult = new SegmentScanResult { HasContent = false, IsEndSegment = false };

            Block childBlock = null;
            BlockCollection blocks = currentSegment.Block.ChildBlocks;

            for (int i = 0; i < blocks.Count; i++)
            {
                childBlock = blocks[i];

                Scope scope = null;
                ScopeCollection scopeCollection = childBlock.ScopePatterns;
                for (int index = 0; index < scopeCollection.Count; index++)
                {
                    scope = scopeCollection[index];

                    PatternScanResult patternScanResult = scope.Start.IndexIn(row.Text, startPosition, scope.CaseSensitive, this.Separators);
                    int currentPosition = patternScanResult.Index;

                    if (segmentScanResult.HasContent != false && currentPosition >= segmentScanResult.Position || patternScanResult.Token == "")
                        continue;

                    segmentScanResult.Pattern = scope.Start;
                    segmentScanResult.Position = currentPosition;
                    segmentScanResult.Token = patternScanResult.Token;
                    segmentScanResult.HasContent = true;
                    segmentScanResult.Block = childBlock;
                    segmentScanResult.Scope = scope;

                    if (scope.NormalizeCase == true)
                    {
                        if (scope.Start.IsComplex == false)
                            segmentScanResult.Token = scope.Start.StringPattern;
                    }
                }
            }

            if (segmentScanResult.HasContent == false)
                return new SegmentScanResult();

            return segmentScanResult;
        }

        #endregion

        #region Public

        /// <summary>
        /// Initializes the DefaultParser with the given file.
        /// </summary>
        /// <param name="syntaxFile">File that contains the language definition.</param>
        public void Init(string syntaxFile)
        {
            if (syntaxFile == "")
                return;

            if (syntaxFile.ToLower().EndsWith(syntaxFileExtension) == false)
                syntaxFile += syntaxFileExtension;

            this.Language = new LanguageReader().Load(syntaxFile);
        }

        /// <summary>
        /// Initializes the DefaultParser with the given file and separators.
        /// </summary>
        /// <param name="syntaxFile">File that contains the language definition.</param>
        /// <param name="separators">Separators that the DefaultParser should use when parsing.</param>
        public void Init(string syntaxFile, string separators)
        {
            if (syntaxFile.ToLower().EndsWith(syntaxFileExtension) == false)
                syntaxFile += syntaxFileExtension;

            this.Language = new LanguageReader().Load(syntaxFile, separators);
        }

        /// <summary>
        /// Initializes the DefaultParser with the given language.
        /// </summary>
        /// <param name="language">Language that the DefaultParser should use.</param>
        public void Init(Language language)
        {
            this.Language = language;
        }

        /// <summary>
        /// Preview-parses the line at the given index.
        /// </summary>
        /// <param name="rowIndex">Index of the line to parse.</param>
        public void ParsePreviewLine(int rowIndex)
        {
            Row row = this.Document[rowIndex];

            row.Clear();
            row.Add(row.Text);
        }

        /// <summary>
        /// Parses the line at the given index.
        /// </summary>
        /// <param name="rowIndex">Index of the line to parse.</param>
        /// <param name="parseKeywords">Whether to parse keywords.</param>
        public void ParseLine(int rowIndex, bool parseKeywords)
        {
            this.InternalParseLine(rowIndex, parseKeywords);
            if (this.Language != null)
            {
                if (version != this.Language.Version)
                {
                    this.Language.UpdateLists();
                    version = this.Language.Version;
                }
            }

            this.Document.InvokeRowParsed(this.Document[rowIndex]);
        }

        #endregion

        #endregion

        /// <summary>
        /// Initializes a new instance of DefaultParser.
        /// </summary>
        public DefaultParser()
        {
        }
    }
}
